#!/usr/bin/env python2
#-*- coding: utf-8 -*-

import sys
import json
import math
import traceback
import logging
import types
import base64

#from sqlalchemy import Table, Column, Integer,\
#        String, db.Boolean, db.DateTime, db.Text, schema, Date,
from sqlalchemy import Sequence, VARBINARY
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import deferred
from sqlalchemy.sql.elements import and_
from sqlalchemy.orm import class_mapper

from Ump import utils, db
from Ump.utils import yy, conv_float, byte2GB
from Ump.umptypes import UmpPath

from Ump.objs import kbase

import utils as db_utils

from flask_sqlalchemy import SQLAlchemy
from Ump.objs.db.session import get_session


# db = SQLAlchemy()

app_db = None
def init_app_db(app):
    global app_db

    db.init_app(app)
    app_db = db

    return db
    
    
filternum = lambda strobj: int(filter(str.isdigit, str(strobj)))


class ORMMethodBase(object):

    def __init__(self, kw_dict={}, *arg, **kw):
        for name, value in kw_dict.iteritems():
            setattr(self, name, value)

    def update(self, values, session=None):
        for k, v in values.iteritems():
            setattr(self, k, v)
        self.save(session=session)

    def save(self, session=None):
        if session is None:
            session = get_session()
        session.add(self)
        try:
            session.commit()
            #session.flush()
        except IntegrityError, e:
            print '#'*100
            traceback.print_exc()
            session.rollback()
            print '#'*100
            db_utils.reraise(sys.exc_info(), 'after session rollback')
        return self

    def delete(self, session=None):
        self.update({'deleted': 1, 'deleted_at': db_utils.todaynow(), 'deleted_friend': self.id})

        try:
            self.relationships_delete(session)
        except Exception,e:
            pass

        return True

    def hard_delete(self, session=None):
        if session is None:
            session = get_session()

        session.delete(self)
        session.commit()
        return True

    def fake_delete(self, session=None):
        return self.update({'fake_deleted': 1, 'fake_deleted_at': db_utils.todaynow()})

    def restore(self, session=None):
        return self.update({'fake_deleted': 0, 'fake_deleted_at': None})

    def relationships_delete(self, session=None):
        delete_relationships_table = ['host', 'volume', 'pool']
        if self.__tablename__ not in delete_relationships_table:
            return
        relationships = class_mapper(self.__class__).relationships.keys()
        for key in relationships :
            relations = getattr(self, key)
            if not isinstance(relations, list):
                continue
            if key in ['events', 'alerts', 'protection_domains'] :
                continue

            for obj in getattr(self, key):
		obj.update({'deleted': 1, 'deleted_at': db_utils.todaynow(), 'deleted_friend': self.id})

    def get_one(self, id_or_spec=None):
        if isinstance(id_or_spec, dict):
            self._username_to_id(id_or_spec)
        else:
            id_or_spec = {'id': id_or_spec}

        id_or_spec.update(deleted=False)
        query = self.query.filter_by(**id_or_spec)
        return query.first()

#    @inspect_func
    def get_list(self, spec={}, skip=0, limit=None, order=None, desc=False, read_deleted=False):
        #self._username_to_id(spec)

        if isinstance(spec, dict):
            if not read_deleted:
                spec.update(deleted=False)

            if spec.get('read_deleted') == 'all':
                spec.pop('read_deleted', None)
                spec.pop('deleted', None)

            self.query.filter_by(**spec)
        if not order:
            if hasattr(self, 'atime'):
                order = self.atime
                desc = True
        query = self._rebuild_query(self.query, skip, limit, order, desc)

        # print '--- model %s spec %s skip %s limit %s order %s desc %s' % (model, spec, skip, limit, order, desc)
        return query.all()

    def _rebuild_query(self, query, skip=0, limit=None, order=None, desc=False):
        print order
        if order:
            if desc:
                query = query.order_by(order.desc())
            else:
                query = query.order_by(order)
        if limit:
            query = query.offset(skip).limit(limit)
        return query

#    def __setitem__(self, key, value):
#        setattr(self, key, value)
#
#    def __getitem__(self, key):
#        return getattr(self, key)

    def get_status(self):
        if self.deleted: 
            return None
        else: 
            return self.status

    def get_json(self, attr, suffix_str="_str"):
        attr_str = attr + suffix_str
        value_str = object.__getattribute__(self, attr_str)
        if value_str is None:
            return None

        try:
            return json.loads(value_str)
        except ValueError:
            return None

    def is_valid(self):
        return not self.deleted


class UssBase(ORMMethodBase):
    """
    All child-class method default filter_by deleted=0 when query,
    unless you set the child-class's is_check_deleted = 1.
    """

    __table_args__ = {'mysql_engine':'InnoDB', 'mysql_charset':'utf8'}
    __table_initialized__ = False
    #__tablename__ = 'umpbase'
    __mapper_args__ = {'polymorphic_identity': 'umpbase'}

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    description = db.Column(db.String(255))
    status = db.Column(db.String(255))
    created_at = db.Column(db.DateTime, default=db_utils.todaynow)
    updated_at = db.Column(db.DateTime, onupdate=db_utils.todaynow)
    deleted_at = db.Column(db.DateTime)
    deleted = db.Column(db.Boolean, default=False)
    fake_deleted = db.Column(db.Boolean, default=False)
    fake_deleted_at = db.Column(db.DateTime)
    deleted_friend = db.Column(db.String(255), default='nerd')#在删除的时候把该值置为自己的id 
    extra = db.Column(db.Text)

    @classmethod
    def _base_filters(self, obj):
        """
        By default, if you query a table,  only the items with
        value 0 in field 'deleted' can be returned;
        if you want to get all the items in the tables,
        you can first set the attribute `is_check_deleted` of models' class to 1;
        for example, if you want to query all the volumes
        whether they are marked as deleted or not in the database,
        you can do as following::

            models.Volume.is_check_deleted = 1
            volumes = models.Volume.query.all()

        """
        return and_(obj.deleted==0) 



class ChapBase(object):
    chap_name = db.Column(db.String(64))
    chap_password = db.Column(db.String(64))


class LockableBase(object):
    locked_at = db.Column(db.DateTime, default=db_utils.todaynow)
    is_locked = db.Column(db.Boolean, default=False)


class BootableBase(object):
    launched_at = db.Column(db.DateTime, default=db_utils.todaynow)
    terminated_at = db.Column(db.DateTime, onupdate=db_utils.todaynow)


class Root(object):
    def __init__(self):
        super(Root, self).__init__()

        # storage policies
        self.repnum = 3
        self.quota = None 
        self.pdomains = [] 
        self.tier = None 

        # cluster:1
        self.custer_id = 1
        self.cluster = None 
    
    @property
    def sp_repnum(self):
        return int(self.repnum)

    @property
    def sp_quota(self):
        if self.quota is not None:
            return int(self.quota)
        return None 

    @property
    def disk_total(self):
        # TODO close session
        if self.cluster:
            return self.cluster.disk_total
        return None 

    @property
    def disk_used(self):
        if self.cluster:
            return self.cluster.disk_used
        return None 


## GLOBALS ROOT Object

ROOT = Root()


class Token(UssBase, db.Model):
    __tablename__ = 'token'

    __table_args__ = (db.UniqueConstraint("token",
                                          "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    token = db.Column(db.String(64))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    user = db.relationship("User", backref=db.backref('tokens', order_by=id),
                        primaryjoin='and_(User.id == Token.user_id,'
                                    'User.deleted == False,'
                                    'Token.deleted == False)')
    @property
    def username(self):
        return self.user.name if self.user else None


users_and_protection_domains = db.Table(
    "users_and_protection_domains",
    db.Column("user_id", db.Integer, db.ForeignKey("user.id")),
    db.Column("protection_domain_id", db.Integer, db.ForeignKey("protection_domain.id")),
)


user_role = db.Table(
    "user_role",
    db.Column("user_id", db.Integer, db.ForeignKey("user.id")),
    db.Column("role_id", db.Integer, db.ForeignKey("role.id")),
)


class User(UssBase, ChapBase, db.Model):
    """"""
    __tablename__ = "user"

    __table_args__ = (db.UniqueConstraint("name",
                                         "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    fullname = db.Column(db.String(255))
    password = db.Column(db.String(255))
    quota = db.Column(db.String(255))
    repnum = db.Column(db.Integer, default=2)
    telephone = db.Column(db.String(255))

    oplogs = db.relationship("Oplog", backref=db.backref('user', order_by=id),
            primaryjoin='and_(User.id == Oplog.user_id,'
            'Oplog.deleted == False)')

    snapshot_policys = db.relationship("SnapshotPolicy", backref=db.backref('user', order_by=id),
            primaryjoin='and_(User.id == SnapshotPolicy.user_id,'
            'SnapshotPolicy.deleted == False)')

    protection_domains = db.relationship(
        "ProtectionDomain",  
        backref=db.backref("users"),
        secondary=users_and_protection_domains
    )
    roles = db.relationship(
        "Role",
        backref=db.backref("users"),
        secondary=user_role
    )


    def __repr__(self):
        return "<User('%s:%s')>" % (self.id, self.name)
        ql/elements

    @property
    def sp_repnum(self):
        if self.repnum:
            return self.repnum
        return ROOT.sp_repnum

    @property
    def sp_quota(self):
        if self.quota:
            return int(self.quota)
        return ROOT.sp_quota

    @property
    def free_quota(self):
        pool_use_quota = 0
        volume_use_quota = 0
        if self.pools:
            for pool in self.pools:
                if pool.quota:
                    pool_use_quota += int(pool.quota)
                else:
                    for volume in pool.volumes:
                        volume_use_quota += int(volume.size_gb)

            free = self.sp_quota - pool_use_quota - volume_use_quota
            return free
        else:
            return self.sp_quota

    @property
    def sp_pdomains(self):
        if not self.protection_domains:
            return []
        else:
            return self.protection_domains

    @property
    def sp_chap_name(self):
        if self.chap_name:
            return self.chap_name
        return self.name

    @property
    def sp_chap_password(self):
        if self.chap_password:
            return self.chap_password
        return str().zfill(12)

    @property
    def disk_total(self):
        total = 0
        pds = self.sp_pdomains
        if pds:
            for pd in pds:
                total += pd.disk_total
        else:
            total = ROOT.disk_total
        return total

    @property
    def disk_used(self):
        used = 0
        pds = self.sp_pdomains
        if pds:
            for pd in pds:
                used += pd.disk_used
        else:
            # TODO cluster_id
            used = ROOT.disk_used if ROOT.disk_used else 0
        return used

    def dump(self, **kw):
        d = {
            'id': self.id,
            'name': self.name,
            'fullname': self.fullname,
            'telephone': self.telephone,
            'repnum': self.repnum,
            'quota': self.quota,
            'sp_repnum': self.sp_repnum,
            'sp_quota': self.sp_quota,
            'sp_pdomains': [pd.id for pd in self.protection_domains],
            # 'sp_pdomains': self.protection_domains,
            'sp_chap_name': self.sp_chap_name,
            'sp_chap_password': self.sp_chap_password,
            'disk_total': self.disk_total,
            'disk_used': self.disk_used,
            'created_at': self.created_at,
        }

        if self.roles:
            d['role'] = self.roles[0].name

        return d

    def hash_password(self, text):
        self.password = text

    def verify_password(self, text):
        pass


class Cluster(UssBase, db.Model):
    """"""
    __tablename__ = 'cluster'
    __table_args__ = (db.UniqueConstraint("cluster_name",
                                         "parent_id",
                                         "deleted_friend"),
                     db.UniqueConstraint('iqn',
                                         "parent_id",
                                         "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    parent_id = db.Column(db.Integer, db.ForeignKey('cluster.id'))
    type = db.Column(db.String(255))

    home = db.Column(db.String(255), default='/opt/fusionstack')
    iqn = db.Column(db.String(255))
    iscsi_port = db.Column(db.Integer)
    repnum = db.Column(db.Integer)
    cluster_name = deferred(db.Column(VARBINARY(255)))
    used = db.Column(db.String(255))
    avail = db.Column(db.String(255))
    capacity = db.Column(db.String(255))
    overbooking = db.Column(db.String(255), default='100')
    system_createtime = db.Column(db.String(20))
    network = db.Column(db.String(255))

    license_capacity = db.Column(db.Text)
    license_permit = db.Column(db.Text)
    license_stat = db.Column(db.Text)
    invalid_date = db.Column(db.DateTime)
    register_date = db.Column(db.DateTime)

    config_str = db.Column(db.Text)
    lichbd_root = db.Column(db.String(255))
    nbd_root = db.Column(db.String(255))

    hosts = db.relationship("Host", 
            backref=db.backref('cluster'),
            primaryjoin='and_(Cluster.id == Host.cluster_id, Host.deleted == False)')

    health = db.relationship("Health", 
            backref=db.backref('cluster'), 
            primaryjoin='and_(Cluster.id == Health.cluster_id, Health.deleted == False)')

    def __repr__(self):
        return "<cluster: %d, %s>" % (self.id, self.cluster_name)

    def __getattribute__(self, attr):
        options = ['config']
        if attr in options:
            attr_str = attr + '_str'
            value_str = object.__getattribute__(self, attr_str)
            if value_str is None:
                return None
            try:
                return json.loads(value_str)
            except ValueError:
                return None
        return object.__getattribute__(self, attr)

    @property
    def cpu_frequency(self):
        hosts = self.hosts
        cpu_frequency = 0
        for host in hosts:
            if host.cpu_frequency:
                cpu_frequency += float(host.cpu_frequency)
        return cpu_frequency

    @property
    def is_nfs(self):
        if self.config:
            return int(self.config.get('nfs.nfsd', 0)) == 1
        return False

    @property
    def disk_total(self):
        total = 0
        for host in self.hosts:
            total += host.disk_total

        return total

    @property
    def disk_used(self):
        used = 0
        for host in self.hosts:
            used += host.disk_used
        return used

    @property
    def disk_total_gb(self):
        return conv_float(byte2GB(self.disk_total))

    @property
    def disk_used_gb(self):
        return conv_float(byte2GB(self.disk_used))
    
    @property
    def disk_percent(self):
        disk_percent = utils.percent(self.disk_used, self.disk_total, 0)    
        return disk_percent

    @property
    def disk_keep(self):
        disk_keep = 20 * 1024 * 1024 * 1024
        if self.config:
            disk_keep = self.config.get('cdsconf.disk_keep')
        return disk_keep

    @property
    def volume_total(self):
        volume_total_ = 0.0
        for volume in self.volumes:
            volume_total_ += int(volume.size)/1000/1000/1000 if volume.size else volume_total_
        return volume_total_

    @property
    def volume_used(self):
        volume_used_ = 0.0
        for volume in self.volumes:
            if volume.repnum is None or volume.repnum == 0:
                volume.repnum = 2
            if volume.used_gb:
                volume_used_ += volume.used_gb * float(volume.repnum)
        return volume_used_
    
    @property
    def usage_mem(self):
        #TODO 要计算self.mem异常的情况
        hosts = self.hosts
        mem_used ,memory_total = 0, 0.1
        for host in hosts:
            mem = host.mem
            if mem is None:
                continue
            if host.mem['MemTotal'] is None or  host.mem['MemFree'] is None:
                continue
            if host.mem['Buffers'] is None or  host.mem['Cached'] is None:
                continue
            mem_used += host.mem_used
            memory_total += filternum(mem['MemTotal'])
        usage_mem = float(mem_used)/float(memory_total)
        memory_total = "%.0f%%" % float(memory_total)
        return usage_mem

    @property
    def mem_total(self):
        hosts = self.hosts
        mem_total = 0
        for host in hosts:
            mem = host.mem
            if mem is None:
                continue
            mem_total += yy(int(host.mem_total), 1024*1024)
    
        return int(mem_total)

    @property
    def usage_cpu(self):
        hosts = self.hosts
        usage_cpu = 0 
        length = 0
        for host in hosts:
            length += 1
            if host.cpu_util != None:
                usage_cpu +=  float(host.cpu_util)*100

        usage_cpu =  "%.2f" % (float(usage_cpu)/length) if length > 0 else "%.2f" %(0)
        return usage_cpu

    @property
    def is_cluster(self):
        return self.type == kbase.PPOOL_TYPE_CLUSTER

    @property
    def iqn2(self):
        if self.config_str:
            s = self.config_str
            d = json.loads(s)
            return d.get('iscsi.iqn')
        return self.iqn

    @property
    def uuid(self):
        if self.config_str:
            s = self.config_str
            d = json.loads(s)
            return d.get('globals.uuid')
        return None

    @property
    def vip(self):
        if self.config_str:
            s = self.config_str
            d = json.loads(s)
            _vip = d.get('iscsi.vip')
            if _vip:
                l = _vip.split('/')
                if len(l) > 0:
                    return l[0]
        return ''

    @property
    def sp_repnum(self):
        if self.repnum:
            return self.repnum
        return ROOT.sp_repnum

    def protocol_root(self, protocol):
        res = 'iscsi'
        if protocol == 'lichbd':
            res = self.lichbd_root
        if protocol == 'nbd':
            res = self.nbd_root
        if protocol.lower() == 'nfs':
            res = 'nfs'
        return res


class ProtectionDomain(UssBase, db.Model):

    __tablename__ = "protection_domain"
    __table_args__ = (db.UniqueConstraint("name",
                                          "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

#    id = db.Column(db.Integer, Sequence("protection_domains_id_seq"), primary_key=True)
    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))


    hosts = db.relationship("Host",
                         backref=db.backref('protection_domain'), lazy='dynamic',
                         primaryjoin='and_(ProtectionDomain.id == Host.protection_domain_id, Host.deleted == False)')

    def __repr__(self):
        return '<ProtectionDomain %s:%s>' % (self.id, self.name)

    @property
    def disk_total(self):
        total = 0
        for host in self.hosts:
            total += host.disk_total
        return total

    @property
    def disk_used(self):
        used = 0
        for host in self.hosts:
            used += host.disk_used
        return used


class Host(UssBase, BootableBase, db.Model):
    """
        volume_id : allocate db.relationships, a host fills a volume. not complete
    """
    __tablename__ = 'host'
    __table_args__ = (db.UniqueConstraint("ip",
                                         "cluster_id",
                                         "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    version = db.Column(db.String(255))
    ip = db.Column(db.String(255))    
    is_join = db.Column(db.Boolean, default=False)
    is_deleting = db.Column(db.Boolean, default=False)
    user = db.Column(db.String(255))
    passwd = db.Column(db.String(255))
    hostname = db.Column(db.Text)
    lichd_status = db.Column(db.String(255))
    uss_status = db.Column(db.Text)
    identity = db.Column(db.String(255))
    cpu_util = db.Column(db.String(255))
    cpu_interval = db.Column(db.Text, default=json.dumps([]))
    cpu_cores_num = db.Column(db.Integer, default=0)
    cpu_frequency = db.Column(db.String(255))
    memory_total = db.Column(db.Text)
    mem_interval = db.Column(db.Text, default=json.dumps([]))
    disk_total_kb = db.Column(db.Text)
    network_str = db.Column(db.Text)
    service_str = db.Column(db.Text)
    uptime_str = db.Column(db.Text)
    mem_str = db.Column(db.Text)
    iops_str = db.Column(db.Text)
    cpu_alert = db.Column(db.Boolean, default=False)
    memory_alert = db.Column(db.Boolean, default=False)

    node_created_at = db.Column(db.DateTime)
    node_invalid_at = db.Column(db.DateTime)
    node_stat_at = db.Column(db.Integer)
    node_register_info = db.Column(db.Text)
    license_stat = db.Column(db.Text)

    log_info = db.Column(db.Text)
    pre_log_info = db.Column(db.Text)
    next_log_info = db.Column(db.Text)

    skip_status = db.Column(db.String(255))

    is_increase =  db.Column(db.Boolean, default=False)
    log_per = db.Column(db.String(255))

    is_send = db.Column(db.Boolean, default=False)
    node_down_at = db.Column(db.DateTime, default=db_utils.todaynow)

    syslog_start_line = db.Column(db.String(25), default=0)
    syslog_time = db.Column(db.DateTime, default=db_utils.yesterdaynow())

    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=False)
    protection_domain_id = db.Column(db.Integer, db.ForeignKey('protection_domain.id'), nullable=True)

    connections = db.relationship("Connection", backref=db.backref('host', order_by=id),
            primaryjoin='and_(Host.id == Connection.host_id,'
            'Connection.deleted == False)')

    @property
    def mem_total(self):
        mem = self.mem
        if mem is None:
            return 0
        mem_total = filternum(self.mem.get('MemTotal',0))
        self.memory_total = str(yy(int(mem_total), 1024*1024))
        return int(mem_total)

    @property
    def mem_used(self):
        mem = self.mem
        if mem is None:
            return None
        if mem['MemTotal'] is None or mem['MemFree'] is None:
            return None
        if mem['Buffers'] is None or mem['Cached'] is None:
            return None
        return filternum(mem['MemTotal']) - filternum(mem['MemFree']) - filternum(mem['Buffers']) - filternum(mem['Cached'])

    @property
    def usage_cpu(self):
        # average for 10 minutes
        try:
            cpu_interval = json.loads(self.cpu_interval)
            cpu_interval = zip(*cpu_interval)[1]
            return round(sum(cpu_interval)/float(len(cpu_interval)), 4)
        except Exception, e:
            return 0

    @property
    def usage_mem(self):
        # average for 10 minutes
        try:
            mem_interval = json.loads(self.mem_interval)
            mem_interval = zip(*mem_interval)[1]
            return round(sum(mem_interval)/float(len(mem_interval)), 4)
        except Exception, e:
            return 0

    @property
    def current_usage_mem(self):
        # TODO 要计算self.mem异常的情况
        mem = self.mem
        usage_mem = 0
        if self.mem_used:
            usage_mem = float(self.mem_used) / filternum(mem['MemTotal'])
        return usage_mem

    @property
    def usage_swap(self):
        #TODO 要考虑self.mem异常的情况
        mem = self.mem
        if mem is None:
            return None
        if mem['SwapFree'] == None or mem['SwapTotal'] == None:
            return 0
        if filternum(mem['SwapTotal']) in [None, 0 , '0']:
            return 0
        return 1 - float(filternum(mem['SwapFree']))/filternum(mem['SwapTotal'])

    @property
    def is_host_low_capacity(self):
        disk, flag = self.disks, True
        if not disk:
            return False
        for dev in disk:
            if dev.isjoin:
                continue
            if int(dev.size) > 20971520:
                flag = False
                break
        return flag

    @property
    def is_host_deleting(self):
        return self.lichd_status == 'deleting'

    @property
    def is_host_stopped(self):
        disks = [ disk for disk in  self.disks if not disk.isjoin]
        stopped_disks = [disk for disk in disks if disk.status == 'stopped']
        return True if disks and len(disks)  == len(stopped_disks) else False

    @property
    def usage_disk(self):
        #TODO 要考虑self.disk异常的情况
        disk = self.disks
        if not disk:
            return None
        if self.is_host_deleting:
            return 0.0
        usage_disk = 0.0
        used, capacity, flag_20g = 0, 0, False
        
        for dev in disk:
            if dev.isjoin:
                continue
            if dev.stat == 'OK':
                tmp_capacity = int(dev.size) 
                capacity += tmp_capacity
                used += int(dev.used)

        if capacity > 0:
            usage_disk = float(used)/float(capacity)
            if usage_disk > 1:
                return 1.0
                #return 1

        return usage_disk

    #add by lp
    @property
    def disk_max_size(self):
        #TODO 要考虑self.disk异常的情况
        disk = self.disks
        if disk is None:
            return 0

        max_size = 0
        for dev in disk:
            if dev.isjoin:
                continue
            if  dev.stat == 'OK':
                disk_size = int(dev.size)
                if disk_size > max_size:
                    max_size = disk_size
        max_size *= 1.024
        return max_size

    @property
    def disk_total(self):
        if self.is_host_deleting:
            return 0

        if self.is_host_low_capacity:
            return 0

        # TODO 要考虑self.disk异常的情况
        disks = self.disks
        if disks is None:
            return 0

        total = 0
        for disk in disks:
            if disk.isjoin:
                continue
            total += int(disk.size)

        return total

    @property
    def disk_used(self):
        #TODO 要考虑self.disk异常的情况
        disks = self.disks
        if disks is None:
            return 0

        used = 0
        for disk in disks:
            if disk.isjoin:
                continue
            if disk.stat == 'OK' :
                used += int(disk.used)
        return used

    @property
    def mds_running(self):
        return self.service_status()[1]

    @property
    def mds_total(self):
        return self.service_status()[0]

    @property
    def cds_total(self):
        return self.service_status()[2]

    @property
    def cds_running(self):
        return self.service_status()[3]

    def service_status(self):
        services = json.loads(self.uss_status) if self.uss_status else {}
        ret_val = dict()
        mds_total = mds_running = 0
        cds_total = cds_running = 0

        for service in services:
            is_runing =  self.status == 'running'

            if 'name' in service.keys() and 'mds' in service['name'].split('/'):
                mds_total += 1
                if service['running'] and is_runing:
                    mds_running += 1
                mds_status = '%s/%s' % (mds_running, mds_total)
                ret_val.update({'mds_status': mds_status})
            elif 'name' in service.keys() and 'cds' in service['name'].split('/'):
                cds_total += 1
                if service['running'] and is_runing:
                    cds_running += 1
                cds_status = '%s/%s' % (cds_running, cds_total)
                ret_val.update({'cds_status': cds_status})

        return mds_total, mds_running, cds_total, cds_running

    def __getattribute__(self, attr):
        options = ['service', 'uptime', 'cpu', 'mem', 'iops','disk']
        if attr in options:
            return self.get_json(attr)
        return object.__getattribute__(self, attr)
    
    def __repr__(self):
        return "%s ›› %s" % (self.id, self.ip)

    def view_dict(self):
        if self.is_join:
            return {'id': str(self.id),
                'ip': self.ip, 
                'info': self.info,
                'status': self.status,
                'is_join': self.is_join,
                'service': self.service, 'uptime': self.uptime,
                'cpu': self.cpu, 'mem': self.mem,
                }
        else:
            return {'id': str(self.id),
                'ip': self.ip,
                'info': self.info,
                'status': self.status,
                'is_join': self.is_join,
                'service': None, 'uptime': None,
                'cpu': None, 'mem': None, 'disk': None,
                }

    @property
    def _license_total(self):
        disk = self.disks
        capacity = all_size = disknum = 0
        for dev in disk:
            if dev.isjoin:
                continue
            if int(dev.size) > 20971520:
                capacity += ((int(dev.size) - 10 * 1024 * 1024* 1024))
                disknum += 1
            else:
                all_size += int(dev.size)

        if capacity < 0:
            capacity = 0

        if capacity < all_size:
            capacity = all_size
        return capacity

    @property
    def license_total(self):
        capacity = self._license_total
        return math.ceil(capacity/1000000000.0) if capacity else capacity

    @property
    def deleting_dev(self):
        deleting_dev = [disk.device for disk in self.disks if disk.stat == 'deleting' ]
        return deleting_dev

    ########## add for permission by zkz
    def full_path(self):
        return '%s/%s' % (self.cluster.full_path(), self.name)


class Disk(UssBase, db.Model):
    """
    Host 表下的disk_str 字段 根据其属性建立的一个表格
    """
    __tablename__ = 'disk'
    __table_args__ = (db.UniqueConstraint("device","host_id",
                                          "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})
    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    use_p = db.Column(db.String(255))
    stat = db.Column(db.String(255), default='OK')
    pre_stat = db.Column(db.String(255), default='OK')
    used = db.Column(db.String(255), default='0')
    size = db.Column(db.String(255), default='0')
    avail = db.Column(db.String(255), default='0')
    raid = db.Column(db.Text)
    raid_missing = db.Column(db.String(255))
    array = db.Column(db.String(255))
    raid_cache = db.Column(db.Text)
    disk_cache = db.Column(db.Text)
    device = db.Column(db.String(255))
    adp_name = db.Column(db.Text)
    adp = db.Column(db.String(255))
    tier = db.Column(db.String(25))
    disk_number = db.Column(db.String(25))
    isjoin = db.Column(db.Boolean, default=False)
    mountpoint = db.Column(db.String(255))
    format = db.Column(db.String(255))
    interface = db.Column(db.String(255))
    free = db.Column(db.String(255))
    partions = db.Column(db.Text)
    bbu_info = db.Column(db.Text)
    cache_warn = db.Column(db.Text)
    serial_number = db.Column(db.String(255))
    drop_recode_path = db.Column(db.String(255))
    light_stat = db.Column(db.String(255), default='off')
    media_type = db.Column(db.String(255), default='unknown')


    host_id = db.Column(db.Integer, db.ForeignKey('host.id'), nullable=False)
    host = db.relationship("Host", backref=db.backref('disks', order_by=id),
                        primaryjoin = 'and_(Host.id == Disk.host_id,'
                                      'Disk.deleted ==False)')

    def __repr__(self):
        return '<disk : %s, %s>'%(self.id, self.device)

    @property
    def host_ip(self):
        host_ip = self.host.ip if self.host  else ''
        return host_ip

    @property
    def partions_show(self):
        partions = []
        if self.partions is not  None:
            partions = json.loads(self.partions)
        return partions

    @property
    def enable_delete(self):
        return True if not self.host.deleting_dev else False

    @property
    def real_size(self):
        return  float(self.size) if self.size else 0
        #return int(self.used) + int(self.avail) if self.used and self.avail and self.stat != 'Failed' else 0

    @property
    def real_used(self):
        real_size = self.real_size
        if self.used and int(self.used) == 0 and  real_size > 0:
            return 0.0001
        return int(self.used) / float(real_size) if real_size > 0 and self.used and self.stat != 'Failed' else 1.0

    @property
    def page_show_size(self):
        size = self.real_size
        size = byte2GB(size)
        #return "%.2fT" % self.conv_float(size / 1024.0) if size > 1000 else "%.2fG" % self.conv_float(size)
        return "%.0fT" % self.conv_float(size / 1024.0) if size > 1000 else "%.0fG" % self.conv_float(size)
    @property
    def page_show_usage(self):
        real_used = self.real_used * 100
        if self.stat == 'Failed':
            return '%.0f' % 0
            #return '%.2f' % 0
        if not self.used :
            #return '%.2f' % 0
            return '%.0f' % 0
        if int(self.size) < 20971520:
            #return '%.2f' % 100
            return '%.0f' % 100

        if real_used > 0 :
            per = self.conv_float(real_used)
        else :
            per = 100
        if per > 100:
            per = 100
        #return '%.2f' % per
        return '%.0f' % per

    def conv_float(self, x, decimal=2):
        #只保留2位，不进位
        assert(isinstance(x, float))
        a, b = str(x).split('.')
        return float(a + '.' + b[:decimal])


pools_and_protection_domains = db.Table(
    "pools_and_protection_domains",
    db.Column("pool_id", db.Integer, db.ForeignKey("pool.id")),
    db.Column("protection_domain_id", db.Integer, db.ForeignKey("protection_domain.id")),
)


class Pool(UssBase, ChapBase, db.Model):
    '''处理和存储相关的pool'''
    __tablename__ = 'pool'
    __table_args__ = (db.UniqueConstraint("name",
                                          "user_id",
                                          "protocol",
                                          "cluster_id",
                                          "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
   # name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    parent = db.Column(db.Text)
    ports = db.Column(db.String(255))
    protocol = db.Column(db.String(255))
    repnum = db.Column(db.Integer, default=2)
    ec_data = db.Column(db.String(10))
    ec_code = db.Column(db.String(10))
    quota = db.Column(db.String(255))
    priority = db.Column(db.String(10))
    provisioning = db.Column(db.String(255))

    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 

    user = db.relationship("User", backref=db.backref('pools', order_by=id),
            primaryjoin='and_(User.id == Pool.user_id,'
            'Pool.deleted == False)')

    cluster = db.relationship("Cluster", backref=db.backref('pools', order_by=id),
            primaryjoin='and_(Cluster.id == Pool.cluster_id,'
            'Pool.deleted == False)')

    protection_domains = db.relationship("ProtectionDomain",
            backref=db.backref("pools"),
            secondary=pools_and_protection_domains
    )


    @property
    def protocol_root(self):
        protocol_root = 'iscsi'
        if self.cluster:
            return self.cluster.protocol_root(self.protocol)
        return protocol_root

    @property
    def path(self):
        return UmpPath(self.name, protocol=self.protocol, username=self.username)

    @property
    def username(self):
        return self.user.name

    @property
    def realname(self):
        return self.path.long_pool_name

    @property
    def sp_chap_name(self):
        if self.chap_name:
            return self.chap_name
        return self.user.sp_chap_name

    @property
    def sp_chap_password(self):
        if self.chap_password:
            return self.chap_password
        return self.user.sp_chap_password

    @property
    def sp_repnum(self):
        if self.repnum:
            return int(self.repnum)
        if self.cluster:
            return self.cluster.sp_repnum

    @property
    def sp_quota(self):
        if self.quota:
            return int(self.quota)
        if self.user:
            return self.user.sp_quota

    @property
    def sp_pdomains(self):
        if self.protection_domains:
            return self.protection_domains
        return self.user.sp_pdomains

    @property
    def use_quota(self):
        volumes_quota = 0
        for volume in self.volumes:

            volumes_quota += volume.size_gb
        return volumes_quota

    @property
    def disk_total(self):
        total = 0
        pds = self.sp_pdomains
        if pds:
            for pd in pds:
                total += pd.disk_total
        else:
            total = self.user.disk_total
        return total

    @property
    def disk_used(self):
        used = 0
        pds = self.sp_pdomains
        if pds:
            for pd in pds:
                used += pd.disk_used
        else:
            used = self.user.disk_used
        return used

    def disk_free(self):
        # TODO
        if self.sp_quota and self.sp_quota > 0:
            used = 0
            for v in self.volumes:
                used += int(v.size)

            free = self.sp_quota * 1024*1024*1024 - used
        else:
            free = self.disk_total - self.disk_used
        print '--- disk_free', type(self.sp_quota), self.sp_quota, free
        return free

    def __repr__(self):
        return '<Pool %s:%s>' % (self.id, self.name)


volume_and_snapshot_policy = db.Table(
    "volume_and_snapshot_policy",
    db.Column("volume_id", db.Integer, db.ForeignKey("volume.id")),
    db.Column("snapshot_policy_id", db.Integer, db.ForeignKey("snapshot_policy.id")),
)


class Connection(UssBase, db.Model):
    __tablename__ = 'connection'

    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    address = db.Column(db.String(255))

    volume_id = db.Column(db.Integer, db.ForeignKey('volume.id'), nullable=False)
    host_id = db.Column(db.Integer, db.ForeignKey('host.id'), nullable=True)

    def __repr__(self):
        return '<Connection %s:%s>' % (self.id, self.name)


class Volume(UssBase, ChapBase, LockableBase, db.Model):
    """
        mode:lichbd, iscsi
    """
    __tablename__ = 'volume'

    __table_args__ = (db.UniqueConstraint("name", "pool_id", "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    status = db.Column(db.String(255), default='normal')
    size = db.Column(db.String(255))
    used = db.Column(db.String(255))
    mode = db.Column(db.String(10)) 
    clone_type = db.Column(db.String(255))
    # realpath = db.Column(db.Text)
    is_share = db.Column(db.Boolean)
    chunkid = db.Column(db.Text)
    is_boot = db.Column(db.Boolean)
    repnum = db.Column(db.Integer, default=2)
    ec_data = db.Column(db.String(4))
    ec_code = db.Column(db.String(4))
    protocol = db.Column(db.String(255))
    provisioning = db.Column(db.String(255))
    analysis_latency = db.Column(db.String(255))
    priority = db.Column(db.String(10))
    # iqn = db.Column(db.String(255))
    snapshot_time = db.Column(db.String(255))

    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 
    user = db.relationship("User", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(User.id == Volume.user_id,'
            'Volume.deleted == False)')

    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=False)
    pool_id = db.Column(db.Integer, db.ForeignKey('pool.id'), nullable=False)
    qos_id = db.Column(db.Integer, db.ForeignKey('QOS.id'), nullable=True)
    
    vgroup_id = db.Column(db.Integer, db.ForeignKey('vgroup.id'), nullable=True)
    snapshot_id = db.Column(db.Integer, db.ForeignKey('snapshot.id'), nullable=True)
    access_policy_id = db.Column(db.Integer, db.ForeignKey('access_policy.id'), nullable=True)
    protection_domain_id = db.Column(db.Integer, db.ForeignKey('protection_domain.id'), nullable=True)

    connections = db.relationship("Connection", backref=db.backref('volume', order_by=id),
            primaryjoin='and_(Volume.id == Connection.volume_id,'
            'Connection.deleted == False)')

    access_policy = db.relationship("AccessPolicy", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(AccessPolicy.id == Volume.access_policy_id,'
            'Volume.deleted == False)')

    pd = db.relationship("ProtectionDomain", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(ProtectionDomain.id == Volume.protection_domain_id,'
            'Volume.deleted == False)')

    qos = db.relationship("QOS", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(QOS.id == Volume.qos_id,'
            'Volume.deleted == False)')

    cluster = db.relationship("Cluster", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(Cluster.id == Volume.cluster_id,'
            'Volume.deleted == False)')

    fake_cluster = db.relationship("Cluster", backref=db.backref('no_fake_volumes', order_by=id),
            primaryjoin='and_(Cluster.id == Volume.cluster_id,'
            'Volume.fake_deleted == False)')

    # TODO if volumes is big
    pool = db.relationship("Pool", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(Pool.id == Volume.pool_id,'
            'Volume.deleted == False)')
    
    snapshot = db.relationship("Snapshot", backref=db.backref('volumes', order_by=id),
            primaryjoin='and_(Snapshot.id == Volume.snapshot_id,'
            'Volume.deleted == False)')

    snapshot_policys = db.relationship(
        "SnapshotPolicy",
        backref=db.backref("volumes"),
        secondary=volume_and_snapshot_policy
    )

    def __repr__(self):
        return '<Volume: id: %s, pool_id: %s, name: %s, deleted: %s>' % (
            self.id, self.pool_id, self.name, self.deleted)

    @property
    def protocol_root(self):
        protocol_root = 'iscsi'
        if self.cluster:
            return self.cluster.protocol_root(self.protocol)
        return protocol_root

    @property
    def path(self):
        return UmpPath('%s/%s' % (self.pool.name, self.name), protocol=self.protocol, username=self.username)

    @property
    def iqn(self):
        if not self.user:
            raise Exception('user %s' % self.user_id)
        if not self.pool:
            raise Exception('pool %s' % self.pool_id)
        return '%s:%s:%s.%s' % (self.cluster.iqn2, self.user.name, self.pool.name, self.name)

    @property
    def sp_chap_name(self):
        if self.chap_name:
            return self.chap_name
        return self.pool.sp_chap_name

    @property
    def sp_chap_password(self):
        if self.chap_password:
            return self.chap_password
        return self.pool.sp_chap_password

    @property
    def size_gb(self):
        unit = 1000
        return float(self.size) / unit / unit / unit
    @property
    def username(self):
        return self.user.name

    @property
    def long_volume_name(self):
        return self.path.long_volume_name

    @property
    def source_snapshot(self):
        if self.snapshot:
            return self.snapshot.path
        else:
            return ''

    @property
    def sp_repnum(self):
        if self.repnum:
            return self.repnum
        if self.pool:
            return self.pool.sp_repnum
        return 3

    @property
    def size_gb(self):
        return int(byte2GB(self.size, unit=1000)) if self.size else 0

    @property
    def used_gb(self):
        return int(byte2GB(self.used, unit=1000)) if self.used else 0

    @property
    def connection_show(self):
        addresses = [x.address for x in self.connections]
        return ",".join(addresses)

    def view_dict(self):
        rs = {}
        attr_skip = ['metadata']
        for i in dir(self):
            if not i.startswith('_') and not callable(getattr(self, i)):
                rs[i] = getattr(self, i)
        for a_skip in attr_skip:
            del rs[a_skip]
        return rs

    def is_clone(self):
        return self.clone_type == 'clone'

    def __getattribute__(self, attr):
        options = ['connection']
        if attr in options:
            return self.get_json(attr)
        elif attr == 'size':
            return int(super(Volume, self).__getattribute__(attr))
        return super(Volume, self).__getattribute__(attr)


class Message(UssBase, db.Model):
    """"""
    __tablename__ = 'message'

    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))

    #('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
    level = db.Column(db.String(255))
    category = db.Column(db.String(255))
    event = db.Column(db.String(255))
    info = db.Column(db.Text)
    #在消息源的时间
    launched_at = db.Column(db.DateTime)
    #入库的时间
    record_at = db.Column(db.DateTime)
    is_handled = db.Column(db.Boolean, default=False)
    evevt_obj = db.Column(db.String(255))

    host = db.Column(db.String(255))  # , db.ForeignKey('hosts.id'))
    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=True)
    cluster = db.relationship("Cluster", backref=db.backref('messages', order_by=id))

    def __repr__(self):
        return '<Message %s:%s>'%(self.id, self.description)

class Oplog(UssBase, db.Model):
    """"""
    __tablename__ = 'oplog'

    id = db.Column(db.Integer, primary_key=True)
    operation = db.Column(db.String(255))  
    category = db.Column(db.String(255))
    resource = db.Column(db.String(255))
    detail = db.Column(db.Text)
    is_handled = db.Column(db.Boolean, default=False)
    oplog_obj = db.Column(db.Text)

    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 
    username = db.Column(db.String(255))

    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=True)
    cluster = db.relationship("Cluster", backref=db.backref('oplogs', order_by=id))
    host_id = db.Column(db.Integer, db.ForeignKey('host.id'), nullable=True)
    host = db.relationship("Host", backref=db.backref('oplogs',  order_by=id))

    def __repr__(self):
        return '<Oplog %s:%s>'%(self.id, self.operation)


class WebSession(UssBase, db.Model):
    """ """
    __tablename__ = 'websession'
    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    session_id = db.Column(db.String(1000))  
    atime = db.Column(db.String(1000)) 
    data = db.Column(db.Text)  


class IOPS(UssBase, db.Model):
    """
    """
    __tablename__ = 'iops'

    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'},)

    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    _iops = db.Column(db.Integer, default=0)
    _iops_read = db.Column(db.Integer, default=0)
    _iops_write = db.Column(db.Integer, default=0)
    _swallow_spit = db.Column(db.Integer, default=0)
    _swallow = db.Column(db.Integer, default=0)
    _spit = db.Column(db.Integer, default=0)
    _iops_data = db.Column(db.Text, default=json.dumps([]))
    _read_data = db.Column(db.Text, default=json.dumps([]))
    _write_data = db.Column(db.Text, default=json.dumps([]))
    _swallow_spit_data = db.Column(db.Text, default=json.dumps([]))
    _swallow_data = db.Column(db.Text, default=json.dumps([]))
    _spit_data = db.Column(db.Text, default=json.dumps([]))
    pool_id = db.Column(db.Integer, nullable=True)

    cluster_id = db.Column(db.Integer, nullable=True)
    volume_id = db.Column(db.Integer, nullable=True)

    def __repr__(self):
        return '<IOPS: %s, %s>'%(self.id, self.name)


class VolumeIops(UssBase, db.Model):

    __tablename__ = 'volume_iops'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'},)

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
#    name = deferred(db.Column(VARBINARY(255)))
    _iops = db.Column(db.Integer, default=0)
    _read = db.Column(db.Integer, default=0)
    _write = db.Column(db.Integer, default=0)
    _in_out = db.Column(db.Integer, default=0)
    _in = db.Column(db.Integer, default=0)
    _out = db.Column(db.Integer, default=0)
    timestamp = db.Column(db.Integer, default=0)
    volume_id = db.Column(db.Integer, db.ForeignKey('volume.id'), nullable=True)
    volume = db.relationship("Volume", backref=db.backref('iops', order_by=id),
            primaryjoin='and_(Volume.id == VolumeIops.volume_id,'
            'VolumeIops.deleted == False)')

    def __repr__(self):
        return '<volume_iops : %s, %s>'%(self.id, self.volume.name)


class HostIops(UssBase, db.Model):

    __tablename__ = 'host_iops'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'},)

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
#    name = deferred(db.Column(VARBINARY(255)))
    _iops = db.Column(db.Integer, default=0)
    _read = db.Column(db.Integer, default=0)
    _write = db.Column(db.Integer, default=0)
    _in_out = db.Column(db.Integer, default=0)
    _in = db.Column(db.Integer, default=0)
    _out = db.Column(db.Integer, default=0)
    timestamp = db.Column(db.Integer, default=0)
    host_id = db.Column(db.Integer, db.ForeignKey('host.id'), nullable=True)
    host = db.relationship("Host", backref=db.backref('iops', order_by=id),
            primaryjoin='and_(Host.id == HostIops.host_id,'
            'HostIops.deleted == False)')

    def __repr__(self):
        return '<host_iops : %s, %s>'%(self.id, self.host.name)


class PoolIops(UssBase, db.Model):

    __tablename__ = 'pool_iops'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'},)

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
#    name = deferred(db.Column(VARBINARY(255)))
    _iops = db.Column(db.Integer, default=0)
    _read = db.Column(db.Integer, default=0)
    _write = db.Column(db.Integer, default=0)
    _in_out = db.Column(db.Integer, default=0)
    _in = db.Column(db.Integer, default=0)
    _out = db.Column(db.Integer, default=0)
    timestamp = db.Column(db.Integer, default=0)
    pool_id = db.Column(db.Integer, db.ForeignKey('pool.id'), nullable=True)
    pool = db.relationship("Pool", backref=db.backref('iops', order_by=id),
            primaryjoin='and_(Pool.id == PoolIops.pool_id,'
            'PoolIops.deleted == False)')

    def __repr__(self):
        return '<pool_iops : %s, %s>'%(self.id, self.pool.name)


class Latency(UssBase, db.Model):
    __tablename__ = 'latency'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'},)

    id = db.Column(db.Integer, primary_key=True)
    latency_val = db.Column(db.String(255))
    read_latency_val = db.Column(db.String(255))
    write_latency_val = db.Column(db.String(255))
    latency_data = db.Column(db.Text, default=json.dumps([]))
    write_latency_data = db.Column(db.Text, default=json.dumps([]))
    read_latency_data = db.Column(db.Text, default=json.dumps([]))
    timestamp = db.Column(db.Integer, default=0)
    cluster_id = db.Column(db.Integer, nullable=True)

    def __repr__(self):
        return '<latency: %s>' % (self.id)

    def __getattribute__(self, attr):
        options = ['latency', 'write_latency', 'read_latency']
        if attr in options:
            return self.get_json(attr, '_data')
        return object.__getattribute__(self, attr)


class VolumeLatency(UssBase, db.Model):

    __tablename__ = 'volume_latency'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'},)

    id = db.Column(db.Integer, primary_key=True)
#    name = deferred(db.Column(VARBINARY(255)))
    read_latency = db.Column(db.Integer, default=0)
    read_last = db.Column(db.Integer, default=0)
    write_latency = db.Column(db.Integer, default=0)
    write_last = db.Column(db.Integer, default=0)
    timestamp = db.Column(db.Integer, default=0)
    volume_id = db.Column(db.Integer, db.ForeignKey('volume.id'), nullable=True)
    volume = db.relationship("Volume", backref=db.backref('latencies', order_by=id),
            primaryjoin='and_(Volume.id == VolumeLatency.volume_id,'
            'VolumeLatency.deleted == False)')

    def __repr__(self):
        return '<volume_latency : %s, %s>'%(self.id, self.volume.name)


class AccessPolicy(UssBase, db.Model):
    """
    """
    __tablename__ = 'access_policy'

    __table_args__ = (db.UniqueConstraint("name",
                                          "user_id",
                                          "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    #name = deferred(db.Column(VARBINARY(255)))
    iprange = db.Column(db.Text)
    initiatorName = db.Column(db.Text)

    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 
    user = db.relationship("User", backref=db.backref('access_policys', order_by=id),
            primaryjoin='and_(User.id == AccessPolicy.user_id,'
            'AccessPolicy.deleted == False)')

    def __repr__(self):
        return '<access_policy : %s, %s>'%(self.id, self.name)

    @property
    def username(self):
        return self.user.name


class QOS(UssBase, db.Model):
    """
    """
    __tablename__ = 'QOS'

    __table_args__ = (db.UniqueConstraint("name",
                                          "user_id",
                                          "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    throt_iops_avg = db.Column(db.String(255))
    throt_burst_iops_max  = db.Column(db.String(255))
    throt_burst_iops_time  = db.Column(db.String(255))
    throt_mbps_avg = db.Column(db.String(255))
    throt_burst_mbps_max  = db.Column(db.String(255))
    throt_burst_mbps_time  = db.Column(db.String(255))

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship("User", backref=db.backref('qoses', order_by=id),
            primaryjoin='and_(User.id == QOS.user_id,'
            'QOS.deleted == False)')

    def __repr__(self):
        return '<QOS: %s, %s>'%(self.id, self.name)

    @property
    def username(self):
        return self.user.name


class VGroup(UssBase, db.Model):

    __tablename__ = 'vgroup'
    __table_args__ = (db.UniqueConstraint("name",
                                          "user_id",
                                          "deleted_friend"),
                     {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))

    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 
    user = db.relationship("User", backref=db.backref('vgroups', order_by=id),
            primaryjoin='and_(User.id == VGroup.user_id,'
            'VGroup.deleted == False)')

    volumes = db.relationship("Volume", backref=db.backref('vgroup', order_by=id),
            primaryjoin='and_(VGroup.id == Volume.vgroup_id,'
            'Volume.deleted == False)')

    cgsnapshots = db.relationship("CGSnapshot", backref=db.backref('vgroup', order_by=id),
            primaryjoin='and_(VGroup.id == CGSnapshot.vgroup_id,'
            'CGSnapshot.deleted == False)')

    def __repr__(self):
        return '<VGroup: %s, %s>'%(self.id, self.name)

    @property
    def username(self):
        return self.user.name

    @property
    def protocol(self):
        if self.volumes:
            return self.volumes[0].protocol
        return 'iscsi'


class CGSnapshot(UssBase, db.Model):

    __tablename__ = 'cgsnapshot'
    __table_args__ = (db.UniqueConstraint("name",
                                          "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    #name = deferred(db.Column(VARBINARY(255)))
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))

    vgroup_id = db.Column(db.Integer, db.ForeignKey('vgroup.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 

    
    def __repr__(self):
        return '<CGSnapshot: %s, %s>' % (self.id, self.name)
    

class Snapshot(UssBase, db.Model):

    __tablename__ = 'snapshot'
    __table_args__ = (db.UniqueConstraint("name", "volume_id",
                                         "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    user = deferred(db.Column(db.Integer))
    name = db.Column(db.String(255))

    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 
    volume_id = db.Column(db.Integer, db.ForeignKey('volume.id'), nullable=True)

    volume = db.relationship("Volume", backref=db.backref('snapshots', order_by=id),
            primaryjoin='and_(Volume.id == Snapshot.volume_id,'
            'Snapshot.deleted == False)')

    description = db.Column(db.Text)
    def __repr__(self):
        return '<Snapshot: %s, %s>'%(self.id, self.name)

    @property
    def path(self):
        vp = self.volume.path
        return UmpPath('%s/%s@%s' % (vp.pool_name, vp.vol_name, self.name), protocol=vp.protocol, username=vp.username)

    def has_clone_volumes(self):
        for vol in self.volumes:
            if vol.is_clone():
                return True
        return False


class SnapshotPolicy(UssBase, db.Model):

    __tablename__ = 'snapshot_policy'
    __table_args__ = (db.UniqueConstraint("name" , "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    snapshot_time = db.Column(db.String(255))
    count = db.Column(db.String(64))
    unit = db.Column(db.String(64))
    snapshot_desc = db.Column(db.Text)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 

    def __repr__(self):
        return '<SnapshotPolicy %s:%s>'%(self.id, self.name)

    @property
    def username(self):
        return self.user.name


class Alert(UssBase, db.Model):
    """"""
    __tablename__ = 'alert'

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))
    level = db.Column(db.String(255))
    returncode = db.Column(db.String(32))
    category = db.Column(db.String(255))
    detail = db.Column(db.Text)
    position = db.Column(db.String(255))
    is_handled = db.Column(db.Boolean, default=False)
    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=True)
    cluster = db.relationship("Cluster", backref=db.backref('alerts', order_by=id))
    host_id = db.Column(db.Integer, db.ForeignKey('host.id'), nullable=True)
    host = db.relationship("Host", backref=db.backref('alerts', order_by=id))
    already_send_mail = db.Column(db.Boolean, default=False)

    def __repr__(self):
        return '<Alert %s:%s>'%(self.id, self.name)


role_permission = db.Table(
    "role_and_permission",
    db.Column("role_id", db.Integer, db.ForeignKey("role.id")),
    db.Column("permission_id", db.Integer, db.ForeignKey("permission.id")),
)


class Permission(UssBase, db.Model):

    __tablename__ = 'permission'
    __table_args__ = (db.UniqueConstraint("resource_type",
                                          "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    resource_id = db.Column(db.Integer)
    resource_type = db.Column(db.String(64))
    operation = db.Column(db.String(64))

    roles = db.relationship(
        "Role",
        backref=db.backref("permissions"),
        secondary=role_permission
    )

    def __cmp__(self, other):
        if self.user_id == other.user_id and self.resource_id == other.resource_id and self.resource_type == other.resource_type:
            return False
        else:
            return True

    def delete(self, session=None):
        #session = self.current_session(session)
        session = self.get_session(session)
        session.delete(self)
        session.flush()

    def __repr__(self):
        return "<Permission(%d, %d, '%s')>" % (self.user_id, self.resource_id, self.resource_type)


class Role(UssBase, db.Model):
    """"""

    __tablename__ = "role"
    __table_args__ = (db.UniqueConstraint("name",
                                          "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    #name = deferred(db.Column(VARBINARY(255)))
    name = db.Column(db.String(255))

    def __repr__(self):
        return "<Role('%s:%s')>" % (self.id, self.name)


rrserver_rrclient = db.Table(
    "remote_replication_server_and_client",
    db.Column("rrserver_id", db.Integer, db.ForeignKey("remote_replication_server.id")),
    db.Column("rrclient_id", db.Integer, db.ForeignKey("remote_replication_client.id")),
)


class RRServer(UssBase, db.Model):

    __tablename__ = "remote_replication_server"
    __table_args__ = (db.UniqueConstraint("address",
                                          "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(64))
    user_id = db.Column(db.Integer)
    password = db.Column(db.String(128))
    address = db.Column(db.String(64))
    port = db.Column(db.Integer)

    rrclients = db.relationship(
        "RRClient",
        backref=db.backref("rrservers"),
        secondary=rrserver_rrclient
    )

    def __repr__(self):
        return "<RRServer('%s:%s')>" % (self.id, self.name)


class RRClient(UssBase, db.Model):

    __tablename__ = "remote_replication_client"
    __table_args__ = (db.UniqueConstraint("server", "src_volume_path", "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(64))
    user_id = db.Column(db.Integer)
    password = db.Column(db.String(128))
    address = db.Column(db.String(64))
    port = db.Column(db.Integer)
    server = db.Column(db.String(64))
    src_volume_path = db.Column(db.String(64))
    dst_volume_path = db.Column(db.String(64))
    interval = db.Column(db.Integer)
    qos = db.Column(db.Integer)
    keep = db.Column(db.Integer)

    def __repr__(self):
        return "<RRClient('%s:%s')>" % (self.id, self.name)

    @property
    def rrserver_id(self):
        return self.rrservers[0].id if self.rrservers else -1


class Sysconfig(UssBase, db.Model):
    
    __tablename__ = "sysconfig"
    __table_args__ = (db.UniqueConstraint("key", "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    module = db.Column(db.String(255))
    key = db.Column(db.String(255))
    value = db.Column(db.String(255))
 
    def __repr__(self):
        return "<Sysconfig('%s:%s')>" % (self.id, self.name)


class SysconfigForUMP(UssBase, db.Model):

    __tablename__ = "sysconfig_for_ump"

    id = db.Column(db.Integer, primary_key=True)
    category = db.Column(db.String(64))
    value_setting = db.Column(db.Text)
    unit = db.Column(db.String(64))
    group_name = db.Column(db.String(64))

    def __repr__(self):
        return "<SysconfigForUMP('%s:%s')>" % (self.id, self.name)


class Health(UssBase, db.Model):

    __tablename__ = "health"
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})
    
    id = db.Column(db.Integer, primary_key=True)
    chunk_recovery_offline = db.Column(db.String(255), default=0)
    chunk_need_recovery = db.Column(db.String(255), default=0) 
    chunk_recovery_success = db.Column(db.String(255), default=0)
    chunk_recovery_lost = db.Column(db.String(255), default=0)
    chunk_recovery_fail = db.Column(db.String(255), default=0)
    node_offline = db.Column(db.String(255), default=0) 
    last_scan = db.Column(db.String(255), default='')
    chunk_recovery_total = db.Column(db.String(255), default=0)
    disk_offline = db.Column(db.String(255), default=0)

    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=False)

    def __repr__(self):
        return "<Health('%s:%s')>" % (self.id, self.name)


class Task(UssBase, db.Model):

    __tablename__ = "task"
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})
    id = db.Column(db.Integer, primary_key=True)
    uuid = db.Column(db.String(255))
    host = db.Column(db.String(255))
    status = db.Column(db.String(255))
    finished_at = db.Column(db.DateTime)
    target = db.Column(db.Text)
    detail = db.Column(db.Text)
    target_id = db.Column(db.Integer)
    model = db.Column(db.String(255))
    is_show = db.Column(db.Boolean, default=True)

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
    user = db.relationship("User", backref=db.backref('tasks', order_by=id),
            primaryjoin='and_(User.id == Task.user_id,'
            'Task.deleted == False)')

    is_finished = db.Column(db.Boolean, default=False)
    timeout = db.Column(db.Integer) #seconds

    def __repr__(self):
        return "<Task('%s:%s')>" % (self.id, self.name)


class ScheduleJob(UssBase, db.Model):
    __tablename__ = "schedule_job"
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    group_name = db.Column(db.String(255))
    host = db.Column(db.String(255))
    category = db.Column(db.String(255))
    schedule_type = db.Column(db.String(255))
    schedule_string = db.Column(db.String(255))
    class_name = db.Column(db.String(255))
    context = db.Column(db.Text)
    owner = db.Column(db.String(255))
    retryable = db.Column(db.String(255))
    enabled = db.Column(db.Boolean, default=True)
    is_finished = db.Column(db.Boolean, default=False)

    run_date = db.Column(db.DateTime)

    #interval type job params
    every = db.Column(db.Integer)
    period = db.Column(db.String(64))

    #cron type job params
    second = db.Column(db.String(255), default='*')
    minute = db.Column(db.String(64), default='*')
    hour = db.Column(db.String(64), default='*')
    day = db.Column(db.String(64), default='*')
    month = db.Column(db.String(64), default='*')
    year = db.Column(db.String(64), default='*')
    day_of_week = db.Column(db.String(64), default='*')
    day_of_month = db.Column(db.String(64), default='*')
    month_of_year = db.Column(db.String(64), default='*')

    #cron and interval type job params
    start_date = db.Column(db.DateTime) 
    end_date = db.Column(db.DateTime) 

    user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 

    sysconfig_for_ump_id = db.Column(db.Integer, db.ForeignKey('sysconfig_for_ump.id'),  nullable=True) 

    sysconfig_for_ump = db.relationship("SysconfigForUMP", backref=db.backref('jobs', order_by=id),
            primaryjoin='and_(SysconfigForUMP.id == ScheduleJob.sysconfig_for_ump_id,'
            'ScheduleJob.deleted == False)')

    @property
    def context_dict(self):
        res = {}
        if self.context:
            res = json.loads(self.context)
            res['schedule_job_id'] = self.id
            res['owner'] = self.owner
        return res

    def __repr__(self):
        return "<ScheduleJob('%s:%s')>" % (self.id, self.class_name)


class ScheduleJobReport(UssBase, db.Model):

    __tablename__ = "schedule_job_report"
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    owner = db.Column(db.String(255))
    type = db.Column(db.String(64))

    schedule_job_id = db.Column(db.Integer, db.ForeignKey('schedule_job.id'))

    schedule_job = db.relationship("ScheduleJob", backref=db.backref('results', order_by=id),
                            primaryjoin='and_(ScheduleJob.id == ScheduleJobReport.schedule_job_id,'
                                        'ScheduleJobReport.deleted == False)')

class Mail(UssBase, db.Model):

    __tablename__ = "mail"
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    mail = db.Column(db.String(255))
    password = db.Column(db.String(255))
    server = db.Column(db.String(255))
    port = db.Column(db.Integer)
    use_smtp = db.Column(db.Boolean, default=True)
    protocol = db.Column(db.String(64))

    def __repr__(self):
        return "<mail: %d, %s>" % (self.id, self.mail)

    @property
    def get_password(self):
        return base64.b64decode(self.password)


class Customer(UssBase, db.Model):

    __tablename__ = "customer"
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    company = db.Column(db.String(255))
    contact = db.Column(db.String(255))
    telephone = db.Column(db.String(255))
    address = db.Column(db.String(255))
    callhome = db.Column(db.Boolean, default=False)

    def __repr__(self):
        return "<customer: %d, %s>" % (self.id, self.name)

class Folder(UssBase, db.Model):
    """folder for nas.
    """
    __tablename__ = 'folder'
    __table_args__ = (db.UniqueConstraint("name", "user_id", "cluster_id", "deleted_friend"),
                      {'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))

    repnum = db.Column(db.Integer, default=2)
    ec_data = db.Column(db.String(10))
    ec_code = db.Column(db.String(10))

    cluster_id = db.Column(db.Integer, db.ForeignKey('cluster.id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    access_policy_id = db.Column(db.Integer, db.ForeignKey('access_policy.id'), nullable=True)

    user = db.relationship("User", backref=db.backref('folders', order_by=id),
            primaryjoin='and_(User.id == Folder.user_id,'
            'Folder.deleted == False)')

    cluster = db.relationship("Cluster", backref=db.backref('folders', order_by=id),
            primaryjoin='and_(Cluster.id == Folder.cluster_id,'
            'Folder.deleted == False)')

    access_policy = db.relationship("AccessPolicy", backref=db.backref('folders', order_by=id),
                                 primaryjoin='and_(AccessPolicy.id == Folder.access_policy_id,'
                                             'Folder.deleted == False)')

    @property
    def username(self):
        return self.user.name

    @property
    def realname(self):
        return '%s:%s' % (self.username, self.name)

    @property
    def ec_show(self):
        if self.ec_data:
            return "%s+%s" % (self.ec_data, self.ec_code)
        else:
            return ''

    def __repr__(self):
        return '<Folder %s:%s>' % (self.id, self.name)


class AlembicVersion(ORMMethodBase, db.Model):
    __tablename__ = 'alembic_version'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset':'utf8'})

    version_num = db.Column(db.String(32), nullable=False, primary_key=True)
    


class License(UssBase, db.Model):
    '''
    license
    '''

    __tablename__ = 'license'
    __table_args__ = ({'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8'})
    id = db.Column(db.Integer, primary_key=True)
    company = db.Column(db.String(255))
    capacity = db.Column(db.Integer)
    contact = db.Column(db.String(255))
    telephone = db.Column(db.String(255))
    email_address = db.Column(db.String(255))

    @property
    def apply_capacity(self):
        return self.capacity

# @models.add_model(models.Host)
# class HostManager(Manager):
def add_model(model):
    def wrapper(cls):
        cls.MODEL = model
        return cls
    return wrapper

DB_TABLE_MAP = {
    'iops': IOPS, 
    'websession': WebSession,
    'volume_iops': VolumeIops,
    'cluster': Cluster,
    'volume': Volume, 
    'host': Host, 
    'user': User,
    'pool': Pool, 
    'message':Message,
    'disk': Disk, 
    'access_policy': AccessPolicy,
    'oplog': Oplog,
    'alert': Alert,
    'QOS': QOS,
    'rrserver': RRServer,
    'rrclient': RRClient,
    'snapshot': Snapshot,
    'vgroup': VGroup,
    'snapshot_group': CGSnapshot,
    'role': Role,
    'permission': Permission,
    'protection_domain': ProtectionDomain,
    'snapshot_policy': SnapshotPolicy,
    'sysconfig': Sysconfig,
    'volume_latency': VolumeLatency,
    'latency': Latency,
    'task': Task,
    'sysconfig_for_ump': SysconfigForUMP,
    'schedule_job': ScheduleJob,
    'schedule_job_report': ScheduleJobReport,
    'customer': Customer,
    'mail': Mail,
    'folder': Folder,
    'license': License,
}

